/////////////////////////////////////////////
//
//  Copyright (c) LEDONET by Dom
//
/////////////////////////////////////////////
#include <tinyhal.h>

#include <stm32f4xx_RCC.h>
#include <stm32f4xx_GPIO.h> 
#include "LCD.h"
#include "SSD1963_CMD.h"
//#include "..\..\..\Targets\Native\STM32F4\DeviceCode\STM32F4_FSMC\STM32F4_FSMC.h"
//#include "C:\MicroFrameworkPK_v4_2\DeviceCode\Targets\Native\STM32F4\DeviceCode\stm32f4xx.h"

//extern unsigned char font_8x8[];

UINT16 LCD_MaxWidth;
UINT16 LCD_MaxHeight;

void LCD_PinConfig(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOG|RCC_AHB1Periph_GPIOB, ENABLE);  
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; // Initialization of Data pins
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(GPIOE, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
	GPIO_Init(GPIOG, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
	GPIO_Init(GPIOF, &GPIO_InitStructure);

	//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4; // Green LED and BLU
	//GPIO_Init(GPIOB, &GPIO_InitStructure);
	
}

void LCD_Init()
{

	LCD_Delay(10);
	
	Set_Rst;		
	Set_Cs;        	// SSD1963 is not selected by default
	Set_nRd;
	Set_nWr;

	Clr_Rst;
	LCD_DelayUs(100);
	Set_Rst;       // release from reset state to sleep state
	
	LCD_DelayUs(250);
	
	Clr_Cs;
	
	LCD_WriteCommand(CMD_SOFT_RESET);				// Soft reset
    LCD_DelayUs(250);
	//Set MN(multipliers) of PLL, VCO = crystal freq * (N+1)
	//PLL freq = VCO/M with 250MHz < VCO < 800MHz
	//The max PLL freq is around 120MHz. To obtain 120MHz as the PLL freq
	LCD_WriteCommand(CMD_SET_PLL_MN);				// Set PLL with OSC = 10MHz (hardware)
													// Multiplier N = 35, VCO (>250MHz)= OSC*(N+1), VCO = 360MHz
	Clr_Cs;
	LCD_WriteData(35);
	LCD_WriteData(2);				    // Divider M = 2, PLL = 360/(M+1) = 120MHz
	LCD_WriteData(0x54);				// Validate M and N values
	Set_Cs;	

	LCD_WriteCommand(CMD_PLL_START);				// Start PLL command
	Clr_Cs;
	LCD_WriteData(0x01);				// enable PLL
	Set_Cs;	

	LCD_DelayUs(500);						// wait stablize
	LCD_WriteCommand(CMD_PLL_START);				// Start PLL command again
	Clr_Cs;
	LCD_WriteData(0x03);				// now, use PLL output as system clock	
	Set_Cs;	

	//Set LSHIFT freq, i.e. the DCLK with PLL freq 120MHz set previously
	//Typical DCLK for TM070RBH10 is 29.23MHz(datasheet), experiment shows 30MHz gives a stable result
	//29.23MHz = 120MHz*(LCDC_FPR + 1)/2^20
	//LCDC_FPR = 255415  (0x3E5B7)
	//Time per line = (DISP_HOR_RESOLUTION+DISP_HOR_PULSE_WIDTH+DISP_HOR_BACK_PORCH+DISP_HOR_FRONT_PORCH)/30 us = 1056/30 = 35.2us
	LCD_WriteCommand(CMD_SET_PCLK); // set for 
	Clr_Cs;
	LCD_WriteData(0x01);
	LCD_WriteData(0x45);
	LCD_WriteData(0x47);
	//LCD_WriteData((LCD_FPR >> 16) & 0xFF);
	//LCD_WriteData((LCD_FPR >> 8) & 0xFF);
	//LCD_WriteData(LCD_FPR & 0xFF);	
	Set_Cs;
	
	//Set panel mode, varies from individual manufacturer
	LCD_WriteCommand(CMD_SET_PANEL_MODE);
	Clr_Cs;
//	LCD_WriteData(0x10);				// set 18-bit for 7" panel TY700TFT800480
	LCD_WriteData(0x28);				// set 18-bit for 4,5" 
	LCD_WriteData(0x20);				// set TTL mode (originale : 0x80)
	LCD_WriteData(0x01);
	LCD_WriteData(0xdf);				//RGB sequence	
	LCD_WriteData(0x01);
	LCD_WriteData(0x0f);
	LCD_WriteData(0x00);
	Set_Cs;

	//Set horizontal period
	#define HPS (DISP_HOR_PULSE_WIDTH+DISP_HOR_BACK_PORCH)
	//#define HT (DISP_HOR_RESOLUTION+DISP_HOR_PULSE_WIDTH+DISP_HOR_BACK_PORCH+DISP_HOR_FRONT_PORCH)
	LCD_WriteCommand(CMD_SET_HOR_PERIOD);
	Clr_Cs;
	LCD_WriteData(0x02);
	LCD_WriteData(0x0d);
	LCD_WriteData(0x00);
	LCD_WriteData(0x2b);
	LCD_WriteData(0x28);
	LCD_WriteData(0x00);
	LCD_WriteData(0x00);
	LCD_WriteData(0x00);
	Set_Cs;
	
	//Set vertical period
	#define VSP (DISP_VER_PULSE_WIDTH+DISP_VER_BACK_PORCH)
	//#define VT (DISP_VER_PULSE_WIDTH+DISP_VER_BACK_PORCH+DISP_VER_FRONT_PORCH+DISP_VER_RESOLUTION)
	LCD_WriteCommand(CMD_SET_VER_PERIOD);
	Clr_Cs;
	LCD_WriteData(0x01);
	LCD_WriteData(0x1d);
	LCD_WriteData(0x00);
	LCD_WriteData(0x0c);
	LCD_WriteData(0x09);
	LCD_WriteData(0x00);
	LCD_WriteData(0x00);
	Set_Cs;
	
	//Set pixel format, i.e. the bpp
	LCD_WriteCommand(CMD_SET_PIXEL_FORMAT );
	Clr_Cs;
	LCD_WriteData(0x60); 				// set 18bpp
	Set_Cs;

	//Set pixel data interface
	LCD_WriteCommand(CMD_SET_PIXEL_DATA_INTERFACE);
	Clr_Cs;
	LCD_WriteData(0x03);				//18-bit(666 format) 
	Set_Cs;

    LCD_WriteCommand(CMD_SET_ADDR_MODE);
	Clr_Cs;
	LCD_WriteData(0x00);
	Set_Cs;
	
	LCD_WriteCommand(CMD_SET_PWM_CONF);
	Clr_Cs;
	LCD_WriteData(0x06);
	LCD_WriteData(0xF0);
	LCD_WriteData(0x01);
	LCD_WriteData(0xFF);
	LCD_WriteData(0x00);
	LCD_WriteData(0x01);
	Set_Cs;
	
	LCD_WriteCommand(CMD_ON_DISPLAY);	
	//Set_Cs;
	//Clr_Cs;
	LCD_WriteCommand(CMD_BLANK_DISPLAY);	        
	//Set_Cs;
	//Clr_Cs;
	LCD_WriteCommand(CMD_ON_DISPLAY);	        
	Set_Cs;     
	LCD_MaxWidth = 480;
	LCD_MaxHeight = 272;
}

void LCD_Delay(UINT32 nCount)
{
  nCount = nCount * 1000;
  while(nCount--);
}

void LCD_DelayUs(UINT32 nCount)
{
  while(nCount--);
}

void LCD_WriteCommand(UINT16 cmd) 
{
	Set_nRd;
	Clr_Rs;
	Clr_nWr;
	Clr_Cs; 
	GPIOG->ODR =((GPIOG->ODR & 0xff00)|(cmd & 0x00ff)); 
	GPIOE->ODR =((GPIOE->ODR & 0x00ff)|(cmd & 0xff00)); 
	//LCD_DelayUs(100);
	Set_Cs; 
	Set_nWr;
	Set_Rs;
};

void LCD_WriteData(UINT16 data)
{
	Set_nRd;
	Set_Rs;
	Clr_nWr;
	Clr_Cs; 
	GPIOG->ODR =((GPIOG->ODR & 0xff00)|(data & 0x00ff)); 
	GPIOE->ODR =((GPIOE->ODR & 0x00ff)|(data & 0xff00)); 
	//LCD_DelayUs(100);
	Set_Cs; 
	Set_nWr;
};

UINT16 LCD_ReadData()
{
	UINT16 data;

	Clr_nRd;
	Set_Rs;
	Set_nWr;
	Clr_Cs; 
	data = GPIOG->ODR; 	
	Set_Cs; 
	Clr_nWr;
	Set_nRd;

	return(data);
}

void LCD_SetPixelAddress(UINT16 x, UINT16 y, UINT16 color)
{ 	
	LCD_SetDrawingWindow( x, y, LCD_MaxWidth, LCD_MaxHeight);
	LCD_WriteCommand(CMD_WR_MEMSTART);
	Clr_Cs;         
	LCD_WriteData(color);
	Set_Cs;
}

void LCD_SetDrawingWindow(UINT16 x, UINT16 y, UINT16 width, UINT16 height)
{

	LCD_WriteCommand(CMD_SET_COLUMN);
	//Clr_Cs;
	LCD_WriteData((x>>8) & 0xff);
	LCD_WriteData(x & 0xff);
	LCD_WriteData(((x + width - 1)>>8) & 0xff);
	LCD_WriteData((x + width - 1) & 0xff);
	//Set_Cs;
	LCD_WriteCommand(CMD_SET_PAGE);
	//Clr_Cs;
	LCD_WriteData((y>>8) & 0xff);
	LCD_WriteData(y & 0xff);
	LCD_WriteData(((y + height - 1)>>8) & 0xff);
	LCD_WriteData((y + height - 1) & 0xff);
	//Set_Cs;
}

void LCD_Clear(UINT16 color)
{
//	u32     counter;
	u32 xcounter, ycounter;

	LCD_SetDrawingWindow(0, 0, LCD_MaxWidth, LCD_MaxHeight);

	LCD_WriteCommand(CMD_WR_MEMSTART);

	//Clr_Cs;
	for(ycounter=0; ycounter < LCD_MaxHeight + 1; ycounter++)
	{
		for(xcounter=0;xcounter < LCD_MaxWidth + 1;xcounter++)
		{
			LCD_WriteData(color);
		}
	}        
	//Set_Cs;
}

void LCD_Line(UINT16 x0, UINT16 y0, UINT16 x1, UINT16 y1, UINT16 color)
{
 	UINT16 x,y;
 	UINT16 dx;// = abs(x1 - x0);
 	UINT16 dy;// = abs(y1 - y0);

	if(y0==y1)
	{
		if(x0<=x1)
		{
			x=x0;
		}
		else
		{
			x=x1;
			x1=x0;
		}
  		while(x <= x1)
  		{
   			LCD_SetPixelAddress(x, y0, color);
   			x++;
  		}
  		return;
	}
	else if(y0>y1)
	{
		dy=y0-y1;
	}
	else
	{
		dy=y1-y0;
	}
 
 	if(x0==x1)
	{
		if(y0<=y1)
		{
			y=y0;
		}
		else
		{
			y=y1;
			y1=y0;
		}
  		while(y <= y1)
  		{
   			LCD_SetPixelAddress(x0,y,color);
   			y++;
  		}
  		return;
	}
	else if(x0 > x1)
 	{
		dx=x0-x1;
  		x = x1;
  		x1 = x0;
  		y = y1;
  		y1 = y0;
 	}
 	else
 	{
		dx=x1-x0;
  		x = x0;
  		y = y0;
 	}

 	if(dx == dy)
 	{
  		while(x <= x1)
  		{

   			x++;
			if(y>y1)
			{
				y--;
			}
			else
			{
   				y++;
			}
   			LCD_SetPixelAddress(x,y,color);
  		}
 	}
 	else
 	{
 		LCD_SetPixelAddress(x, y, color);
  		if(y < y1)
  		{
   			if(dx > dy)
   			{
    			s16 p = dy * 2 - dx;
    			s16 twoDy = 2 * dy;
    			s16 twoDyMinusDx = 2 * (dy - dx);
    			while(x < x1)
    			{
     				x++;
     				if(p < 0)
     				{
      					p += twoDy;
     				}
     				else
     				{
      					y++;
      					p += twoDyMinusDx;
     				}
     				LCD_SetPixelAddress(x, y,color);
    			}
   			}
   			else
   			{
    			s16 p = dx * 2 - dy;
    			s16 twoDx = 2 * dx;
    			s16 twoDxMinusDy = 2 * (dx - dy);
    			while(y < y1)
    			{
     				y++;
     				if(p < 0)
     				{
      					p += twoDx;
     				}
     				else
     				{
      					x++;
      					p+= twoDxMinusDy;
     				}
     				LCD_SetPixelAddress(x, y, color);
    			}
   			}
  		}
  		else
  		{
   			if(dx > dy)
   			{
    			s16 p = dy * 2 - dx;
    			s16 twoDy = 2 * dy;
	    		s16 twoDyMinusDx = 2 * (dy - dx);
    			while(x < x1)
    			{
     				x++;
     				if(p < 0)
	     			{
    	  				p += twoDy;
     				}
     				else
     				{
      					y--;
	      				p += twoDyMinusDx;
    	 			}
     				LCD_SetPixelAddress(x, y,color);
    			}
   			}
	   		else
   			{
    			s16 p = dx * 2 - dy;
    			s16 twoDx = 2 * dx;
	    		s16 twoDxMinusDy = 2 * (dx - dy);
    			while(y1 < y)
    			{
     				y--;
     				if(p < 0)
	     			{
    	  				p += twoDx;
     				}
     				else
     				{
      					x++;
	      				p+= twoDxMinusDy;
    	 			}
     				LCD_SetPixelAddress(x, y,color);
    			}
   			}
  		}
 	}
}

void LCD_Rectangle(UINT16 left, UINT16 top, UINT16 width, UINT16 height, UINT16 color, bool fill)
{
	register UINT16  x,y;
	UINT16  right, bottom;

	if ( width < 1 ) width = 1;
	if ( height < 1 ) height = 1;
	right = left + width - 1;
	bottom = top + height - 1;
	if (!fill) 
	{
		LCD_Line(left, top, left, bottom, color);
		LCD_Line(left, bottom, right , bottom , color);
		LCD_Line(right, bottom, right, top, color);
		LCD_Line(right, top, left, top, color);
	} 
	else
	{ // Filled Rectangle

		LCD_SetDrawingWindow(left, top, width, height);
		LCD_WriteCommand(CMD_WR_MEMSTART);
		Clr_Cs;
		for(y = top; y <= bottom; y++)
		{
			for(x = left; x <= right; x++)
			{
				LCD_WriteData(color);
			}
		}
		Set_Cs;
	}
}

int power (int base, int n) 
{
    int     i,p;
    p = 1;
    for (i = 1; i <= n; ++i)
	p *= base;
    return p;
}


void LCD_PutChar(UINT16 x, UINT16 y, UINT8 Character, UINT16 ForeColor, UINT16 BackColor)
{
        int row = 0;
        int column = 0;
        //int offset = 0; //(Character - 32) * font_8x8[0];
        int font_w, font_h;
		char pixel = 0;

		font_w = Font_Width();
		font_h = Font_Height();
		const UINT8 * ch = Font_GetGlyph(Character);
		LCD_Rectangle( x, y, font_w, font_h, BackColor, TRUE );
        LCD_SetDrawingWindow(x, y, font_w, font_h);
        LCD_WriteCommand(CMD_WR_MEMSTART);
        Clr_Cs;        
		for(row = 0; row < font_h; row++)
		{
			for(column = 0; column < font_w; column++)
			{
				pixel = (ch[row] << column) & 0x80;
				if ( pixel == 0x80 )
					LCD_WriteData(ForeColor);					
				else
					LCD_WriteData(BackColor);
			}
		}
        Set_Cs;
}   


void LCD_Text(UINT16 x, UINT16 y, LPCSTR str, UINT16 len, UINT16 ForeColor, UINT16 BackColor)
{
  UINT8 i;
  
  if ( (len*Font_Width() + x) > LCD_MaxWidth ) // you are going out of screen
  {
	  len = (UINT16)( (LCD_MaxWidth - x) / Font_Width() );
  }
  for (i=0; i<len; i++)
  {
    LCD_PutChar(x + (i*Font_Width()), y, *str++, ForeColor, BackColor);
  }
}

void LCD_Circle(u16 cx, u16 cy, u16 r,u16 color, bool fill)
{
	u16 x,y;
	s16 delta,tmp;
	x = 0;
	y = r;
	delta = 3 - (r<<1);

	while(y > x)
	{
		if(fill)
		{
			LCD_Line(cx+x,cy+y,cx-x,cy+y,color);
			LCD_Line(cx+x,cy-y,cx-x,cy-y,color);
			LCD_Line(cx+y,cy+x,cx-y,cy+x,color);
			LCD_Line(cx+y,cy-x,cx-y,cy-x,color);
		}
		else
		{
			LCD_SetPixelAddress(cx+x,cy+y,color);
			LCD_SetPixelAddress(cx-x,cy+y,color);
			LCD_SetPixelAddress(cx+x,cy-y,color);
			LCD_SetPixelAddress(cx-x,cy-y,color);
			LCD_SetPixelAddress(cx+y,cy+x,color);
			LCD_SetPixelAddress(cx-y,cy+x,color);
			LCD_SetPixelAddress(cx+y,cy-x,color);
			LCD_SetPixelAddress(cx-y,cy-x,color);
		}
		x++;
		if(delta >= 0)
		{
			y--;
			tmp = (x<<2);
			tmp -= (y<<2);
			delta += (tmp+10);
		}
		else
		{
			delta+=((x<<2)+6);
		}
	}
}

void LCD_SetScrollArea(UINT16 x, UINT16 y, UINT16 width, UINT16 height, UINT16 scrollx, UINT16 scrolly, bool up_down)
{
	// x, width, scrollx, updown not used
	u8 l_data, h_data;
	h_data = (u8) (((y + height) & 0xff00) >> 8 );
	l_data = (u8) (((y + height) & 0x00ff));

	LCD_WriteCommand(CMD_SET_SCROLL_START); // set scroll area start
    LCD_WriteData(h_data);
    LCD_WriteData(l_data);

}

void SetPWMBacklight(UINT8 value)
{
	LCD_WriteCommand(CMD_SET_PWM_CONF);
	Clr_Cs;
	LCD_WriteData(0x06);
	LCD_WriteData(value);
	LCD_WriteData(0x01);
	LCD_WriteData(0xFF);
	LCD_WriteData(0x00);
	LCD_WriteData(0x01);
	Set_Cs;
}

INT8 LCD_SetSpecialConfig(INT32 key, INT32 value)
{
/*
	switch (key)
	{
		case 1: //Display Type
			if (value != N18 && value != GAMEO)
				return 0;

			g_SpecialDisplayConfigType = value;

			return 1;

		case 2: //Display BPP
			if (value != 7 && value != 8)
				return 0;

			g_SpecialDisplayConfigBPP = value;

			return 1;

		case 3: //Display Orientation
			if (value != ORIENTATION_ROT_0 && value != ORIENTATION_ROT_90 && value != ORIENTATION_ROT_180 && value != ORIENTATION_ROT_270)
				return 0;

			g_SpecialDisplayConfigOrientation = value;

			return 1;

		case 4: //Display Pin
			g_SpecialDisplayConfigRSPin = value;

			return 1;
		
		//SPI Configuration
		case 5: g_SpecialDisplayConfigSPI.DeviceCS = (GPIO_PIN)value; return 1;
		case 6: g_SpecialDisplayConfigSPI.CS_Active = (BOOL)value; return 1;
		case 7: g_SpecialDisplayConfigSPI.MSK_IDLE = (BOOL)value; return 1;
		case 8: g_SpecialDisplayConfigSPI.MSK_SampleEdge = (BOOL)value; return 1;
		case 9: g_SpecialDisplayConfigSPI.Clock_RateKHz = (UINT32)value; return 1;
		case 10: g_SpecialDisplayConfigSPI.CS_Setup_uSecs = (UINT32)value; return 1;
		case 11: g_SpecialDisplayConfigSPI.CS_Hold_uSecs = (UINT32)value; return 1;
		case 12: g_SpecialDisplayConfigSPI.SPI_mod = (UINT32)value; return 1;
		case 13: g_SpecialDisplayConfigSPI.BusyPin.Pin = (GPIO_PIN)value; return 1;
		case 14: g_SpecialDisplayConfigSPI.BusyPin.ActiveState = (BOOL)value; return 1;

		default:
			return 0;
	}
	*/
	return 0;
}
